package com.sgb.goods.utils.base.fragment;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;

import com.sgb.goods.BR;
import com.sgb.goods.R;
import com.sgb.goods.databinding.GoodsFragmentBaseBinding;
import com.sgb.goods.databinding.GoodsViewLoadErrorBinding;
import com.sgb.goods.databinding.GoodsViewNoDataBinding;
import com.sgb.goods.databinding.GoodsViewNoNetworkBinding;
import com.sgb.goods.utils.base.GoodsSdk;
import com.sgb.goods.utils.base.LoadState;
import com.sgb.goods.utils.base.viewmodel.BaseViewModel;
import com.swgk.core.dialog.DialogHelper;

/**
 * Fragment的基类
 *
 * @param <DB> data binding
 * @param <VM> view model
 * @author cyj
 */
public abstract class BaseFragment<DB extends ViewDataBinding, VM extends BaseViewModel>
        extends Fragment {

    protected DB mDataBinding;
    protected VM mViewModel;
    private int variableId = 0;
    private boolean isFirstInit = true;
    protected Lifecycle.Event currentLifecycleEvent = Lifecycle.Event.ON_ANY;
    private GoodsFragmentBaseBinding mFragmentBaseBinding;
    private GoodsViewLoadErrorBinding mViewLoadErrorBinding;
    private GoodsViewNoNetworkBinding mViewNoNetworkBinding;
    private GoodsViewNoDataBinding mViewNoDataBinding;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (null != getArguments()) {
            handleBundle(getArguments());
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mFragmentBaseBinding = DataBindingUtil.inflate(inflater, R.layout.goods_fragment_base, container, false);
        mDataBinding = DataBindingUtil.inflate(inflater, getLayoutResId(), mFragmentBaseBinding.flContentContainer, true);

        initViewModel();
        baseInitViewModel();
        mDataBinding.setLifecycleOwner(this);

        init();
        initObserve();
        initListener();
        getLifecycle().addObserver(eventObserver);

        return mFragmentBaseBinding.getRoot();
    }

    /**
     * 自定义 LifecycleEventObserver ，官方推荐主要解决当前 fragment 的显示或者生命周期状态
     */
    protected LifecycleEventObserver eventObserver = (source, event) -> {
        this.currentLifecycleEvent = event;
        if (Lifecycle.Event.ON_RESUME == event && isFirstInit) {
            isFirstInit = false;
            lazyLoadData();
        }
    };

    /**
     * 创建 Activity ViewModel。不会进行数据绑定。
     *
     * @param cls Class
     * @param <T> Object
     * @return ViewModel
     */
    public <T extends BaseViewModel> T createParentViewModel(Class<T> cls) {
        return createViewModel(requireActivity(), cls);
    }

    /**
     * 创建ViewModel。会自动进行数据绑定。
     *
     * @param cls        Class
     * @param <T>        Object
     * @param variableId ViewModel在布局文件的variableId，如：BR.viewModel。此值设置会自动进行数据绑定
     * @return ViewModel
     */
    public <T extends BaseViewModel> T createViewModel(Class<T> cls, int variableId) {
        this.variableId = variableId;
        return createViewModel(this, cls);
    }

    /**
     * 创建ViewModel。不会进行数据绑定。
     *
     * @param owner ViewModelStoreOwner
     * @param cls   Class
     * @param <T>   Object
     * @return ViewModel
     */
    final <T extends BaseViewModel> T createViewModel(@NonNull ViewModelStoreOwner owner, Class<T> cls) {
        return new ViewModelProvider(owner, new ViewModelProvider.AndroidViewModelFactory(
                requireActivity().getApplication())).get(cls);
    }

    /**
     * 基础的ViewModel初始化
     */
    private void baseInitViewModel() {
        if (null != mViewModel) {
            // 绑定ViewModel
            mDataBinding.setVariable(variableId, mViewModel);
            // ViewModel订阅生命周期事件
            getLifecycle().addObserver(mViewModel);
            // 添加加载状态 observer
            mViewModel.loadState.observe(getViewLifecycleOwner(), this::addLoadView);
        }
    }

    /**
     * 初始化业务数据及view视图
     */
    protected void init() {
    }

    /**
     * 初始化加载网络/其他数据，该函数在 fragment 显示时只会调用一次
     */
    protected void lazyLoadData() {
    }

    /**
     * 初始化liveData
     */
    protected void initObserve() {
    }

    /**
     * 初始化(view)相关Listener
     */
    protected void initListener() {
    }

    /**
     * 处理Intent
     *
     * @param bundle {@link this#getArguments()}
     */
    protected void handleBundle(Bundle bundle) {
    }

    /**
     * 当前页面的布局资源ID
     *
     * @return @IdRes
     */
    protected abstract int getLayoutResId();

    /**
     * 初始化ViewModel
     */
    protected void initViewModel() {
    }

    /**
     * 添加LoadView到视图
     *
     * @param loadState {@link LoadState}
     */
    private void addLoadView(LoadState loadState) {
        if (!LoadState.LOADING.equals(loadState) && !LoadState.CANCEL_LOADING.equals(loadState)) {
            // 网络加载框不属于UI布局创建
            removeLoadView();
        }
        switch (loadState) {
            case LOADING:
                // 注意：此处一定要替换成布局模式，防止activity跳转或调用activity.start()方式导致实例失效DialogHelper显示不正常
                DialogHelper.showProgressDialog(GoodsSdk.getInstance().getTopActivity(), "数据加载中")
                        .setCanceledOnTouchOutside(loadState.params.isCancelable);
                break;
            case NO_NETWORK:
                addLoadStateViewForLoadState(mViewNoNetworkBinding, R.layout.goods_view_no_network, loadState);
                break;
            case NO_DATA:
                addLoadStateViewForLoadState(mViewNoDataBinding, R.layout.goods_view_no_data, loadState);
                break;
            case ERROR:
                addLoadStateViewForLoadState(mViewLoadErrorBinding, R.layout.goods_view_load_error, loadState);
                break;
            default:
                break;
        }
    }

    /**
     * 移除LoadView
     */
    protected void removeLoadView() {
        int childCount = mFragmentBaseBinding.flContentContainer.getChildCount();
        if (childCount > 1) {
            mFragmentBaseBinding.flContentContainer.removeViews(1, childCount - 1);
        }
    }

    /**
     * 添加LoadState的状态View到业务容器
     *
     * @param obj
     * @param layoutId
     * @param loadState
     */
    private void addLoadStateViewForLoadState(ViewDataBinding obj, int layoutId, LoadState loadState) {
        if (null == loadState.params) {
            return;
        }
        if (null == obj) {
            obj = DataBindingUtil.inflate(getLayoutInflater(), layoutId, mFragmentBaseBinding.flContentContainer, false);
            obj.setVariable(BR.viewModel, mViewModel);
        }
        // 指定原view视图的具体位置来设置覆盖层的属性
        if (loadState.params.left > 0 || loadState.params.top > 0 || loadState.params.right > 0 || loadState.params.bottom > 0) {
            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(-1, -1);
            int screenWidth = mFragmentBaseBinding.flContentContainer.getWidth();
            int screenHeight = mFragmentBaseBinding.flContentContainer.getHeight();
            lp.leftMargin = loadState.params.left;
            lp.topMargin = loadState.params.top;
            lp.rightMargin = screenWidth - loadState.params.right;
            lp.bottomMargin = screenHeight - loadState.params.bottom;
            lp.width = loadState.params.right - loadState.params.left;
            lp.height = loadState.params.bottom - loadState.params.top;
            obj.getRoot().setLayoutParams(lp);
        }
        mFragmentBaseBinding.flContentContainer.addView(obj.getRoot());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mViewModel != null) {
            getLifecycle().removeObserver(mViewModel);
            getLifecycle().removeObserver(eventObserver);
        }
        removeLoadView();
    }
}
